2 Part II Advanced displays

2.1 Preparation

You will find the material for this lecture at https://github.com/cbhurley/CRT2021vis

First install these packages

install.packages(c("GGally", "ISLR2", "naniar"))

and load with

library(GGally)
library(ISLR2)
library(timetk) # for the data
library(palmerpenguins)
library(tidyverse)
library(naniar)

bike <- bike_sharing_daily
bike$season <- c("Winter","Spring", "Summer","Fall")[bike$season]
bike$season <- factor(bike$season, levels = c("Winter","Spring", "Summer","Fall"))  

bike$mnth <- factor(bike$mnth)

bike$holiday <- factor(c("No","Yes")[bike$holiday+1])
bike$workingday <- factor(c("No","Yes")[bike$workingday+1])

bike$yr <- factor(c("2011","2012")[bike$yr+1])

bike$weathersit <- c("clear", "cloudy", "lightP", "heavyP")[bike$weathersit]
bike$weathersit <- factor(bike$weathersit, levels= c("clear", "cloudy", "lightP", "heavyP"))
bike <- droplevels(bike)

2.2 Big data

ISLR2 has an hourly version of the bike data with 8645 observations.

The plot of registered versuscasual users is

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + geom_point()

There is likely lot of overplotting.

Jittering will randomly undo rounding, and using alpha will help too

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + 
  geom_jitter(alpha=.5, size=.5)

Other options are to form 2d bins, count the number of observations in each, and color in proportion to the frequency.

p <- ggplot(data=Bikeshare, aes(x=casual, y=registered))
p+ geom_bin2d()

I prefer to make my colour scale light to dark:

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4")

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

The second plot takes a log of the counts so you get a better spread of colours

p +  
  stat_binhex() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

stat_binhex uses hexagonal binning

A more advanced plot fits a 2d density estimate and draw this as a contour plot.

p + stat_density2d()

# fills in the contours
p + stat_density2d(aes(fill = after_stat(level)), geom = "polygon") 

2.3 Scatterplot matrix

Back to the smaller dataset.

Go to articles tab for info https://ggobi.github.io/ggally/

library(GGally)
ggpairs(bike, columns=c("casual","registered","cnt" ))

You can change what is plotted on lower, upper and diagonal,using lower= etc. Putting in mapping= gives densities for both years separately

ggpairs(bike, mapping = aes(color = yr),
            columns=c("casual","registered","cnt" ),
  lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
  diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

ggpairs can handle factors as well. In this plot yrs is in as a plot variable, and as colour.

ggpairs(bike, mapping = aes(color = yr),
        columns=c("casual","registered","cnt","yr"),
        lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
        diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

2.4 Parallel coordinates

These can be constructed with function ggparcoord from GGally.

But, a newer package called ggpcp is better.

install.packages("devtools")
remotes::install_github("yaweige/ggpcp", build_vignettes = TRUE)

Construction: with a sample of 20 bike observations

This is how you create the parallel coordinate plot in ggpcp

library(ggpcp)
ggplot(bike[s,], aes(color=yr)) + geom_pcp(aes(vars=vars(casual,registered, cnt)))

Here, all variables are scaled to the unit interval individually. Variables that are positively correlated will have parallel line segments, here registered and cnt.

Other scaling options are available eg

ggplot(bike[s,], aes(color=yr)) + geom_pcp(aes(vars=vars(casual,registered, cnt)), method="raw")

In raw, no scaling is done. Here you can see that most users per day are registered.

To see what certain patterns look like in a parallel coordinate plot, let us look at some fake data

x <- rnorm(100)
y <- x+ .3*rnorm(100)
z <- -y+ .1*rnorm(100)
w <- -z+ .5*rnorm(100)
ggplot(data.frame(x,y,z,w))+ geom_pcp(aes(vars=vars(everything())))

Positive correlation exhibits as parallel segments.

Negative correlation has crossing. The stronger the negative correlation the smaller the pinch point.

ggplot(bike, aes(color=workingday)) + geom_pcp(aes(vars=vars(casual,registered, cnt)), alpha=.4)

In the above we see that there is a difference in the casual-registered association for workingday or not. Both groups have positive association but slopes are different. There are more casual users at the weekend.

Verifying with a scatterplot

ggplot(data=bike, aes(x=casual, y=registered, color=workingday)) + geom_point()

The same pattern was evident in the full bike data.

Standard parallel coordinate plots require numeric variables. There are variants that have been defined for categorical variables.

With ggpcp factor variables can be used as an axis variable:

ggplot(bike, aes(vars=vars( workingday,casual,registered, cnt), color=workingday)) + 
  geom_pcp(alpha=.4)

ggplot(bike, aes(vars=vars( workingday,casual,registered, cnt), color=workingday)) + 
   geom_pcp_box(boxwidth = 0.1, fill="grey70")+
    geom_pcp(alpha=.4,boxwidth = 0.1)+
   geom_pcp_text(boxwidth = 0.1)

Other variants of parallel coordinate plots for categorical data are in ggalluvial, ggparallel, but these are for factors only.

2.5 Practice 3

Using the penguins dataset, make a plot of the four dimension variables, colored by species.

Which pair of variables and species has the highest correlation?

Can you find two variables with negative correlation, but positive correlation within each species? This is called Simpson’s paradox.

Then make a parallel coordinate plot of the same four variables, with the same colouring scheme.

Can you identify presence of positive and negative correlation?

In this plot, how does the Gentoo species compare to the other two species?

Finally, make a parallel coordinate plot of species, island, and body_mass_g, coloured by species. How many species are on each island?

2.6 Missing data

Here we use package naniar. This is the dataset for your assignment. All the NAs are marked in grey.

m <- read_csv("marathon.csv",na=c(""," ","NA"))
vis_miss(m)

An upset plot shows the combination of missing values. Most of the NAs involve the club variable.

gg_miss_upset(m[,-c(22,20)])

Check out the visualisations in naniar for more options, eg the vignette at https://cran.r-project.org/web/packages/naniar/vignettes/naniar-visualisation.html

2.7 titanic case study

There were 2201 people aboard the titanic when it sank in 1912. A summary of the people on board and whether they survived or not is given in the titanicanic dataset.

Here are some images as it left its last port of call, Cobh.

Can we figure out from this data which category of passenger had the highest chance of survival?

This case study looks at 4 categorical variables simultaneously: - Survival (yes or no), - class (first, second, third, crew,) - gender (male or female) and - age (child or adult).

First we look at the number of survivors by class

#titanicanic  # a 4d array....
titanic <- as.data.frame(Titanic)
head(titanic)
##   Class    Sex   Age Survived Freq
## 1   1st   Male Child       No    0
## 2   2nd   Male Child       No    0
## 3   3rd   Male Child       No   35
## 4  Crew   Male Child       No    0
## 5   1st Female Child       No    0
## 6   2nd Female Child       No    0
titanic<- titanic[c(17:32,1:16),]

ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col() 

and then the survival rates by class

ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") 

  • There is a big difference in Survival rates across the classes.
  • First class passengers have the highest chance of survival, crew members the lowest.

We can bring in gender with facet_wrap

ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") + 
  facet_wrap( ~ Sex) + ylab("Rate")

  • Third class females do worse than crew females (46% versus 87%), and third class males do worse than crew males (17% versus 22%), although when gender is ignored the situation is reversed.

  • This apparent paradox (Simpson’s paradox) occurs because the vast majority of crew members are male.

  • The overall 3rd class survival rate of 25% is an average of 46% and 17%, but as there are 2.6 times more men than women in 3rd class 17% gets more weight.

  • The overall crew survival rate of 24% is an average of 87% and 22%, but as there are 37 times more men than women in crew class 87% gets very little weight.

Finally, bringing in age

ggplot(data = titanic, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") + 
  facet_wrap( ~ Age+ Sex) + ylab("Rate")

We can get some simplification by showing the survival rate, that is omit the red sections.

titanicR <- 
titanic %>%  group_by(Class, Sex,Age) %>%
summarise(died=Freq[2], survived=Freq[1], rate=survived/sum(Freq))
titanicR
## # A tibble: 16 × 6
## # Groups:   Class, Sex [8]
##    Class Sex    Age    died survived     rate
##    <fct> <fct>  <fct> <dbl>    <dbl>    <dbl>
##  1 1st   Male   Child     0        5   1     
##  2 1st   Male   Adult   118       57   0.326 
##  3 1st   Female Child     0        1   1     
##  4 1st   Female Adult     4      140   0.972 
##  5 2nd   Male   Child     0       11   1     
##  6 2nd   Male   Adult   154       14   0.0833
##  7 2nd   Female Child     0       13   1     
##  8 2nd   Female Adult    13       80   0.860 
##  9 3rd   Male   Child    35       13   0.271 
## 10 3rd   Male   Adult   387       75   0.162 
## 11 3rd   Female Child    17       14   0.452 
## 12 3rd   Female Adult    89       76   0.461 
## 13 Crew  Male   Child     0        0 NaN     
## 14 Crew  Male   Adult   670      192   0.223 
## 15 Crew  Female Child     0        0 NaN     
## 16 Crew  Female Adult     3       20   0.870
ggplot(data = titanicR, aes(x = Class, y = rate)) +
  geom_col(fill="lightblue") + 
  facet_wrap( ~ Age+ Sex) 

One of the examples in the ggpcp vignette has a parallel coordinates version of this data: https://github.com/yaweige/ggpcp

2.8 Practice 4

Explore the survival rates for the data lusitania.csv on https://github.com/cbhurley/CRT2021vis This is a tabulated version of the data, in the same format as titanic. The complete dataset is available on Kaggle.

You can find out more about the shipwreck https://en.wikipedia.org/wiki/RMS_Lusitania

Ci0tLQp0aXRsZTogIkRhdGEgdmlzdWFsaXNhdGlvbiBpbiBSIgphdXRob3I6CiAgLSBuYW1lOiAiQ2F0aGVyaW5lIEh1cmxleSIKICAgIGFmZmlsaWF0aW9uOiBNYXlub290aCBVbml2ZXJzaXR5CiAgICBlbWFpbDogY2F0aGVyaW5lLmh1cmxleUBtdS5pZQpkYXRlOiAiNy85LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBwYW5kb2NfYXJnczogWyItLW51bWJlci1vZmZzZXQ9MSJdCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQogIAotLS0KCjwhLS0gPHN0eWxlIHR5cGU9InRleHQvY3NzIj4gLS0+CjwhLS0gICBib2R5LCB0ZCB7IC0tPgo8IS0tICAgICBmb250LXNpemU6IDE0cHQ7IC0tPgo8IS0tICAgfSAtLT4KPCEtLSBjb2RlLnJ7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0OyAtLT4KPCEtLSB9IC0tPgo8IS0tIHByZSB7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0IC0tPgo8IS0tIH0gLS0+CjwhLS0gPC9zdHlsZT4gLS0+CgoKCgoKCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RikKYGBgCiMgUGFydCBJSSBBZHZhbmNlZCBkaXNwbGF5cwoKIyMgUHJlcGFyYXRpb24KCllvdSB3aWxsIGZpbmQgdGhlIG1hdGVyaWFsIGZvciB0aGlzIGxlY3R1cmUgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgoKRmlyc3QgaW5zdGFsbCB0aGVzZSBwYWNrYWdlcwoKYGBge3IsIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcyhjKCJHR2FsbHkiLCAiSVNMUjIiLCAibmFuaWFyIikpCmBgYAoKYW5kIGxvYWQgd2l0aAoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KHRpbWV0aykgIyBmb3IgdGhlIGRhdGEKbGlicmFyeShwYWxtZXJwZW5ndWlucykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmFuaWFyKQoKYmlrZSA8LSBiaWtlX3NoYXJpbmdfZGFpbHkKYmlrZSRzZWFzb24gPC0gYygiV2ludGVyIiwiU3ByaW5nIiwgIlN1bW1lciIsIkZhbGwiKVtiaWtlJHNlYXNvbl0KYmlrZSRzZWFzb24gPC0gZmFjdG9yKGJpa2Ukc2Vhc29uLCBsZXZlbHMgPSBjKCJXaW50ZXIiLCJTcHJpbmciLCAiU3VtbWVyIiwiRmFsbCIpKSAgCgpiaWtlJG1udGggPC0gZmFjdG9yKGJpa2UkbW50aCkKCmJpa2UkaG9saWRheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJGhvbGlkYXkrMV0pCmJpa2Ukd29ya2luZ2RheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJHdvcmtpbmdkYXkrMV0pCgpiaWtlJHlyIDwtIGZhY3RvcihjKCIyMDExIiwiMjAxMiIpW2Jpa2UkeXIrMV0pCgpiaWtlJHdlYXRoZXJzaXQgPC0gYygiY2xlYXIiLCAiY2xvdWR5IiwgImxpZ2h0UCIsICJoZWF2eVAiKVtiaWtlJHdlYXRoZXJzaXRdCmJpa2Ukd2VhdGhlcnNpdCA8LSBmYWN0b3IoYmlrZSR3ZWF0aGVyc2l0LCBsZXZlbHM9IGMoImNsZWFyIiwgImNsb3VkeSIsICJsaWdodFAiLCAiaGVhdnlQIikpCmJpa2UgPC0gZHJvcGxldmVscyhiaWtlKQpgYGAKCgojIyBCaWcgZGF0YQoKSVNMUjIgaGFzIGFuIGhvdXJseSB2ZXJzaW9uIG9mIHRoZSBiaWtlIGRhdGEgd2l0aCA4NjQ1ICBvYnNlcnZhdGlvbnMuCgpUaGUgcGxvdCBvZiByZWdpc3RlcmVkIHZlcnN1c2Nhc3VhbCB1c2VycyBpcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlcmUgaXMgbGlrZWx5IGxvdCBvZiBvdmVycGxvdHRpbmcuCgpKaXR0ZXJpbmcgd2lsbCByYW5kb21seSB1bmRvIHJvdW5kaW5nLCBhbmQgdXNpbmcgYWxwaGEgd2lsbCBoZWxwIHRvbwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjUsIHNpemU9LjUpCmBgYAoKT3RoZXIgb3B0aW9ucyBhcmUgdG8gZm9ybSAyZCBiaW5zLCBjb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoLCBhbmQgY29sb3IgaW4KcHJvcG9ydGlvbiB0byB0aGUgZnJlcXVlbmN5LgoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgPC0gZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpCnArIGdlb21fYmluMmQoKQpgYGAKCkkgcHJlZmVyIHRvIG1ha2UgbXkgY29sb3VyIHNjYWxlIGxpZ2h0IHRvIGRhcms6CmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIpCgpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYAoKVGhlIHNlY29uZCBwbG90IHRha2VzIGEgbG9nIG9mIHRoZSBjb3VudHMgc28geW91IGdldCBhIGJldHRlciBzcHJlYWQgb2YgY29sb3VycwoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgKyAgCiAgc3RhdF9iaW5oZXgoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYApgc3RhdF9iaW5oZXhgIHVzZXMgaGV4YWdvbmFsIGJpbm5pbmcKCiBBIG1vcmUgYWR2YW5jZWQgcGxvdCBmaXRzIGEgMmQgZGVuc2l0eSBlc3RpbWF0ZSBhbmQgZHJhdyB0aGlzIGFzIGEgY29udG91ciBwbG90LgogCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifSAKcCArIHN0YXRfZGVuc2l0eTJkKCkKYGBgICAKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9Rn0gCiMgZmlsbHMgaW4gdGhlIGNvbnRvdXJzCnAgKyBzdGF0X2RlbnNpdHkyZChhZXMoZmlsbCA9IGFmdGVyX3N0YXQobGV2ZWwpKSwgZ2VvbSA9ICJwb2x5Z29uIikgCmBgYCAgICAKCiMjIFNjYXR0ZXJwbG90IG1hdHJpeAoKQmFjayB0byB0aGUgc21hbGxlciBkYXRhc2V0LgoKR28gdG8gYXJ0aWNsZXMgdGFiIGZvciBpbmZvCjxodHRwczovL2dnb2JpLmdpdGh1Yi5pby9nZ2FsbHkvPgoKCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShHR2FsbHkpCmdncGFpcnMoYmlrZSwgY29sdW1ucz1jKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiApKQpgYGAKCllvdSBjYW4gY2hhbmdlIHdoYXQgaXMgcGxvdHRlZCBvbiBsb3dlciwgdXBwZXIgYW5kIGRpYWdvbmFsLHVzaW5nCmBsb3dlcj1gIGV0Yy4KUHV0dGluZyBpbiBgbWFwcGluZz1gIGdpdmVzIGRlbnNpdGllcyBmb3IgYm90aCB5ZWFycyBzZXBhcmF0ZWx5CgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKICAKZ2dwYWlycyhiaWtlLCBtYXBwaW5nID0gYWVzKGNvbG9yID0geXIpLAogICAgICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiICksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQoKYGBgCgoKYGdncGFpcnNgIGNhbiBoYW5kbGUgZmFjdG9ycyBhcyB3ZWxsLgpJbiB0aGlzIHBsb3QgYHlyc2AgaXMgaW4gYXMgYSBwbG90IHZhcmlhYmxlLCBhbmQgYXMgY29sb3VyLgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKCmdncGFpcnMoYmlrZSwgbWFwcGluZyA9IGFlcyhjb2xvciA9IHlyKSwKICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiLCJ5ciIpLAogICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKIApgYGAKCiMjIFBhcmFsbGVsIGNvb3JkaW5hdGVzCgpUaGVzZSBjYW4gYmUgY29uc3RydWN0ZWQgd2l0aCBmdW5jdGlvbiBgZ2dwYXJjb29yZGAgZnJvbSBHR2FsbHkuCgpCdXQsIGEgbmV3ZXIgcGFja2FnZSBjYWxsZWQgYGdncGNwYCBpcyBiZXR0ZXIuCgpgYGB7ciwgZXZhbD1GfQppbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJ5YXdlaWdlL2dncGNwIiwgYnVpbGRfdmlnbmV0dGVzID0gVFJVRSkKYGBgCgpDb25zdHJ1Y3Rpb246IHdpdGggYSBzYW1wbGUgb2YgMjAgYmlrZSBvYnNlcnZhdGlvbnMKCgoKCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9VCwgZWNobz1GfQoKcyA8LSBzYW1wbGUobnJvdyhiaWtlKSwyMCkKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0wLHNjYWxlPSJ1bmltaW5tYXgiKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0xLHNjYWxlPSJ1bmltaW5tYXgiKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIixzY2FsZT0idW5pbWlubWF4IikKYGBgCgpUaGlzIGlzIGhvdyB5b3UgY3JlYXRlIHRoZSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaW4gYGdncGNwYAoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShnZ3BjcCkKZ2dwbG90KGJpa2VbcyxdLCBhZXMoY29sb3I9eXIpKSArIGdlb21fcGNwKGFlcyh2YXJzPXZhcnMoY2FzdWFsLHJlZ2lzdGVyZWQsIGNudCkpKQpgYGAKCkhlcmUsIGFsbCB2YXJpYWJsZXMgYXJlIHNjYWxlZCB0byB0aGUgdW5pdCBpbnRlcnZhbCBpbmRpdmlkdWFsbHkuClZhcmlhYmxlcyB0aGF0IGFyZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2lsbCBoYXZlIHBhcmFsbGVsIGxpbmUgc2VnbWVudHMsIGhlcmUgcmVnaXN0ZXJlZCBhbmQgY250LgoKT3RoZXIgc2NhbGluZyBvcHRpb25zIGFyZSBhdmFpbGFibGUgZWcKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChiaWtlW3MsXSwgYWVzKGNvbG9yPXlyKSkgKyBnZW9tX3BjcChhZXModmFycz12YXJzKGNhc3VhbCxyZWdpc3RlcmVkLCBjbnQpKSwgbWV0aG9kPSJyYXciKQoKYGBgCkluIHJhdywgbm8gc2NhbGluZyBpcyBkb25lLiBIZXJlIHlvdSBjYW4gc2VlIHRoYXQgbW9zdCB1c2VycyBwZXIgZGF5IGFyZSByZWdpc3RlcmVkLgoKVG8gc2VlIHdoYXQgY2VydGFpbiBwYXR0ZXJucyBsb29rIGxpa2UgaW4gYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QsIGxldAp1cyBsb29rIGF0IHNvbWUgZmFrZSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKeCA8LSBybm9ybSgxMDApCnkgPC0geCsgLjMqcm5vcm0oMTAwKQp6IDwtIC15KyAuMSpybm9ybSgxMDApCncgPC0gLXorIC41KnJub3JtKDEwMCkKZ2dwbG90KGRhdGEuZnJhbWUoeCx5LHosdykpKyBnZW9tX3BjcChhZXModmFycz12YXJzKGV2ZXJ5dGhpbmcoKSkpKQpgYGAKClBvc2l0aXZlIGNvcnJlbGF0aW9uIGV4aGliaXRzIGFzIHBhcmFsbGVsIHNlZ21lbnRzLgoKTmVnYXRpdmUgY29ycmVsYXRpb24gaGFzIGNyb3NzaW5nLiBUaGUgc3Ryb25nZXIgdGhlIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIHRoZSBzbWFsbGVyIHRoZSBwaW5jaCBwb2ludC4KCgoKCmBgYHtyLGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGJpa2UsIGFlcyhjb2xvcj13b3JraW5nZGF5KSkgKyBnZW9tX3BjcChhZXModmFycz12YXJzKGNhc3VhbCxyZWdpc3RlcmVkLCBjbnQpKSwgYWxwaGE9LjQpCgpgYGAKCkluIHRoZSBhYm92ZSB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gdGhlIGNhc3VhbC1yZWdpc3RlcmVkIGFzc29jaWF0aW9uIGZvciB3b3JraW5nZGF5IG9yIG5vdC4KQm90aCBncm91cHMgaGF2ZSBwb3NpdGl2ZSBhc3NvY2lhdGlvbiBidXQgc2xvcGVzIGFyZSBkaWZmZXJlbnQuClRoZXJlIGFyZSBtb3JlIGNhc3VhbCB1c2VycyBhdCB0aGUgd2Vla2VuZC4KClZlcmlmeWluZyB3aXRoIGEgc2NhdHRlcnBsb3QKCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoZGF0YT1iaWtlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCwgY29sb3I9d29ya2luZ2RheSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlIHNhbWUgcGF0dGVybiB3YXMgZXZpZGVudCBpbiB0aGUgZnVsbCBiaWtlIGRhdGEuCgpTdGFuZGFyZCBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIHJlcXVpcmUgbnVtZXJpYyB2YXJpYWJsZXMuIFRoZXJlIGFyZSB2YXJpYW50cyB0aGF0IGhhdmUgYmVlbiBkZWZpbmVkIGZvcgpjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCgpXaXRoIGBnZ3BjcGAgZmFjdG9yIHZhcmlhYmxlcyBjYW4gYmUgdXNlZCBhcyBhbiBheGlzIHZhcmlhYmxlOgoKYGBge3IsZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoYmlrZSwgYWVzKHZhcnM9dmFycyggd29ya2luZ2RheSxjYXN1YWwscmVnaXN0ZXJlZCwgY250KSwgY29sb3I9d29ya2luZ2RheSkpICsgCiAgZ2VvbV9wY3AoYWxwaGE9LjQpCgpnZ3Bsb3QoYmlrZSwgYWVzKHZhcnM9dmFycyggd29ya2luZ2RheSxjYXN1YWwscmVnaXN0ZXJlZCwgY250KSwgY29sb3I9d29ya2luZ2RheSkpICsgCiAgIGdlb21fcGNwX2JveChib3h3aWR0aCA9IDAuMSwgZmlsbD0iZ3JleTcwIikrCiAgICBnZW9tX3BjcChhbHBoYT0uNCxib3h3aWR0aCA9IDAuMSkrCiAgIGdlb21fcGNwX3RleHQoYm94d2lkdGggPSAwLjEpCgoKYGBgCgpPdGhlciB2YXJpYW50cyBvZiBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3RzIGZvciBjYXRlZ29yaWNhbCBkYXRhIGFyZSBpbiBnZ2FsbHV2aWFsLCBnZ3BhcmFsbGVsLCBidXQgdGhlc2UgYXJlIGZvcgpmYWN0b3JzIG9ubHkuCgojIyBQcmFjdGljZSAzCgpVc2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0LCBtYWtlIGEgcGxvdCBvZiAgdGhlIGZvdXIgZGltZW5zaW9uIHZhcmlhYmxlcywgY29sb3JlZCBieSBzcGVjaWVzLgoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GLCBldmFsPUZ9CmdncGFpcnMobmEub21pdChwZW5ndWlucyksIG1hcHBpbmcgPSBhZXMoY29sb3IgPSBzcGVjaWVzKSwKICAgICAgICAgICAgY29sdW1ucz1jKDM6NiksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQpgYGAKV2hpY2ggcGFpciBvZiB2YXJpYWJsZXMgYW5kIHNwZWNpZXMgaGFzIHRoZSBoaWdoZXN0IGNvcnJlbGF0aW9uPwoKQ2FuIHlvdSBmaW5kIHR3byB2YXJpYWJsZXMgd2l0aCBuZWdhdGl2ZSBjb3JyZWxhdGlvbiwgYnV0IHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGhpbiBlYWNoIHNwZWNpZXM/ClRoaXMgaXMgY2FsbGVkIFNpbXBzb24ncyBwYXJhZG94LgoKVGhlbiBtYWtlIGEgcGFyYWxsZWwgY29vcmRpbmF0ZSBwbG90IG9mIHRoZSBzYW1lIGZvdXIgdmFyaWFibGVzLCB3aXRoIHRoZSBzYW1lIGNvbG91cmluZyBzY2hlbWUuCgpDYW4geW91IGlkZW50aWZ5IHByZXNlbmNlIG9mIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBjb3JyZWxhdGlvbj8KCkluIHRoaXMgcGxvdCwgaG93IGRvZXMgdGhlIEdlbnRvbyBzcGVjaWVzIGNvbXBhcmUgdG8gdGhlIG90aGVyIHR3byBzcGVjaWVzPwoKRmluYWxseSwgbWFrZSBhIHBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdCBvZiBzcGVjaWVzLCBpc2xhbmQsIGFuZCBib2R5X21hc3NfZywgY29sb3VyZWQgYnkgc3BlY2llcy4KSG93IG1hbnkgc3BlY2llcyBhcmUgb24gZWFjaCBpc2xhbmQ/CgpgYGB7cixmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG89RiwgZXZhbD1GfQoKZ2dwbG90KHBlbmd1aW5zLCBhZXModmFycz12YXJzKGJpbGxfbGVuZ3RoX21tOmJvZHlfbWFzc19nKSwgY29sb3I9c3BlY2llcykpKwogICAgICAgIGdlb21fcGNwKGFscGhhPS40KQoKCmdncGxvdChwZW5ndWlucywgYWVzKHZhcnM9dmFycyhzcGVjaWVzLCBpc2xhbmQsIHN0YXJ0c193aXRoKCJib2R5IikpLCBjb2xvcj1zcGVjaWVzKSkrCiAgZ2VvbV9wY3BfYm94KGJveHdpZHRoID0gMC4xLCBmaWxsPSJncmV5NzAiKSsKICBnZW9tX3BjcChhbHBoYT0uNCxib3h3aWR0aCA9IDAuMSkrCiAgZ2VvbV9wY3BfdGV4dChib3h3aWR0aCA9IDAuMSkKCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpIZXJlIHdlIHVzZSBwYWNrYWdlIG5hbmlhci4KVGhpcyBpcyB0aGUgZGF0YXNldCBmb3IgeW91ciBhc3NpZ25tZW50LiBBbGwgdGhlIE5BcyBhcmUgbWFya2VkIGluIGdyZXkuCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciJ9Cm0gPC0gcmVhZF9jc3YoIm1hcmF0aG9uLmNzdiIsbmE9YygiIiwiICIsIk5BIikpCnZpc19taXNzKG0pCmBgYAoKQW4gdXBzZXQgcGxvdCBzaG93cyB0aGUgY29tYmluYXRpb24gb2YgbWlzc2luZyB2YWx1ZXMuCk1vc3Qgb2YgdGhlIE5BcyBpbnZvbHZlIHRoZSBjbHViIHZhcmlhYmxlLgoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIifQpnZ19taXNzX3Vwc2V0KG1bLC1jKDIyLDIwKV0pCmBgYAoKQ2hlY2sgb3V0IHRoZSB2aXN1YWxpc2F0aW9ucyBpbiBgbmFuaWFyYCBmb3IgbW9yZSBvcHRpb25zLCBlZyB0aGUgdmlnbmV0dGUgYXQgPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uYW5pYXIvdmlnbmV0dGVzL25hbmlhci12aXN1YWxpc2F0aW9uLmh0bWw+CgoKIyMgdGl0YW5pYyBjYXNlIHN0dWR5CgpUaGVyZSB3ZXJlIDIyMDEgcGVvcGxlIGFib2FyZCB0aGUgdGl0YW5pYyB3aGVuIGl0IHNhbmsgaW4gMTkxMi4KQSBzdW1tYXJ5IG9mIHRoZSBwZW9wbGUgb24gYm9hcmQgYW5kIHdoZXRoZXIgdGhleSBzdXJ2aXZlZCBvciBub3QgaXMgZ2l2ZW4gaW4gdGhlIHRpdGFuaWNhbmljIGRhdGFzZXQuCgpIZXJlIGFyZSBzb21lIGltYWdlcyBhcyBpdCBsZWZ0IGl0cyBsYXN0IHBvcnQgb2YgY2FsbCwgQ29iaC4KCmBgYHtyLCBlY2hvPUYsIGV2YWw9VCwgIG91dC53aWR0aD0iMzAlIixmaWcuYWxpZ249J2NlbnRlcicsIGZpZy5zaG93PSdob2xkJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoYygiZmlncy90aXRhbmljLnBuZyIpKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhjKCJmaWdzL2NvYmgucG5nIikpCmBgYAoKCkNhbiB3ZSBmaWd1cmUgb3V0IGZyb20gdGhpcyBkYXRhIHdoaWNoIGNhdGVnb3J5IG9mIHBhc3NlbmdlciBoYWQgdGhlIGhpZ2hlc3QgY2hhbmNlIG9mIHN1cnZpdmFsPwoKVGhpcyBjYXNlIHN0dWR5IGxvb2tzIGF0IDQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHNpbXVsdGFuZW91c2x5OgotIFN1cnZpdmFsICh5ZXMgb3Igbm8pLCAKLSBjbGFzcyAoZmlyc3QsIHNlY29uZCwgdGhpcmQsIGNyZXcsKQotIGdlbmRlciAobWFsZSBvciBmZW1hbGUpIGFuZCAKLSBhZ2UgKGNoaWxkIG9yIGFkdWx0KS4KCgpGaXJzdCB3ZSBsb29rIGF0IHRoZSBudW1iZXIgb2Ygc3Vydml2b3JzIGJ5IGNsYXNzCmBgYHtyLCAgZmlnLndpZHRoPTQuNSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CiN0aXRhbmljYW5pYyAgIyBhIDRkIGFycmF5Li4uLgp0aXRhbmljIDwtIGFzLmRhdGEuZnJhbWUoVGl0YW5pYykKaGVhZCh0aXRhbmljKQp0aXRhbmljPC0gdGl0YW5pY1tjKDE3OjMyLDE6MTYpLF0KCmdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbCgpIApgYGAKCmFuZCB0aGVuIHRoZSBzdXJ2aXZhbCByYXRlcyBieSBjbGFzcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikgCmBgYAoKCi0gVGhlcmUgaXMgYSBiaWcgZGlmZmVyZW5jZSBpbiBTdXJ2aXZhbCByYXRlcyBhY3Jvc3MgdGhlIGNsYXNzZXMuCi0gRmlyc3QgY2xhc3MgcGFzc2VuZ2VycyBoYXZlIHRoZSBoaWdoZXN0IGNoYW5jZSBvZiBzdXJ2aXZhbCwgY3JldyBtZW1iZXJzIHRoZSBsb3dlc3QuIAoKCldlIGNhbiBicmluZyBpbiBnZW5kZXIgd2l0aCBgZmFjZXRfd3JhcGAKCmBgYHtyLCAgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpICsgCiAgZmFjZXRfd3JhcCggfiBTZXgpICsgeWxhYigiUmF0ZSIpCmBgYAoKLSBUaGlyZCBjbGFzcyBmZW1hbGVzIGRvIHdvcnNlIHRoYW4gY3JldyBmZW1hbGVzICg0NiUgdmVyc3VzIDg3JSksIGFuZAp0aGlyZCBjbGFzcyBtYWxlcyBkbyB3b3JzZSB0aGFuIGNyZXcgbWFsZXMgKDE3JSB2ZXJzdXMgMjIlKSwgYWx0aG91Z2ggd2hlbiBnZW5kZXIgaXMgaWdub3JlZCB0aGUgc2l0dWF0aW9uIGlzCnJldmVyc2VkLgoKLSBUaGlzIGFwcGFyZW50IHBhcmFkb3ggKFNpbXBzb24ncyBwYXJhZG94KSBvY2N1cnMgYmVjYXVzZSB0aGUgdmFzdCBtYWpvcml0eSBvZiBjcmV3IG1lbWJlcnMgYXJlIG1hbGUuCgotIFRoZSBvdmVyYWxsIDNyZCBjbGFzcyBzdXJ2aXZhbCByYXRlIG9mIDI1JSBpcyBhbiBhdmVyYWdlIG9mIDQ2JSBhbmQgMTclLCBidXQgYXMgdGhlcmUgYXJlIDIuNiB0aW1lcwptb3JlIG1lbiB0aGFuIHdvbWVuIGluIDNyZCBjbGFzcyAxNyUgZ2V0cyBtb3JlIHdlaWdodC4KCi0gVGhlIG92ZXJhbGwgY3JldyBzdXJ2aXZhbCByYXRlIG9mIDI0JSBpcyBhbiBhdmVyYWdlIG9mIDg3JSBhbmQgMjJcJSwgYnV0IGFzIHRoZXJlIGFyZSAzNyB0aW1lcwptb3JlIG1lbiB0aGFuIHdvbWVuIGluIGNyZXcgY2xhc3MgODclIGdldHMgdmVyeSBsaXR0bGUgd2VpZ2h0LgoKRmluYWxseSwgYnJpbmdpbmcgaW4gYWdlCmBgYHtyLCAgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD00LjUsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikgKyAKICBmYWNldF93cmFwKCB+IEFnZSsgU2V4KSArIHlsYWIoIlJhdGUiKQpgYGAKCldlIGNhbiBnZXQgc29tZSBzaW1wbGlmaWNhdGlvbiBieSBzaG93aW5nIHRoZSBzdXJ2aXZhbCByYXRlLCB0aGF0IGlzIG9taXQgdGhlIHJlZCBzZWN0aW9ucy4KCgpgYGB7ciwgIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NC41LCBmaWcuYWxpZ249ImNlbnRlciJ9CnRpdGFuaWNSIDwtIAp0aXRhbmljICU+JSAgZ3JvdXBfYnkoQ2xhc3MsIFNleCxBZ2UpICU+JQpzdW1tYXJpc2UoZGllZD1GcmVxWzJdLCBzdXJ2aXZlZD1GcmVxWzFdLCByYXRlPXN1cnZpdmVkL3N1bShGcmVxKSkKdGl0YW5pY1IKCmdncGxvdChkYXRhID0gdGl0YW5pY1IsIGFlcyh4ID0gQ2xhc3MsIHkgPSByYXRlKSkgKwogIGdlb21fY29sKGZpbGw9ImxpZ2h0Ymx1ZSIpICsgCiAgZmFjZXRfd3JhcCggfiBBZ2UrIFNleCkgCgoKICAKYGBgCgpPbmUgb2YgdGhlIGV4YW1wbGVzIGluIHRoZSBgZ2dwY3BgIHZpZ25ldHRlIGhhcyBhIHBhcmFsbGVsIGNvb3JkaW5hdGVzIHZlcnNpb24gb2YgdGhpcyBkYXRhOgo8aHR0cHM6Ly9naXRodWIuY29tL3lhd2VpZ2UvZ2dwY3A+ICAKCgpgYGB7ciwgZWNobz1GLCBldmFsPVQsICBvdXQud2lkdGg9IjUwJSIsZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuc2hvdz0naG9sZCd9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoImZpZ3MvcGNwLnBuZyIpKQpgYGAKCiMjIFByYWN0aWNlIDQKCkV4cGxvcmUgdGhlIHN1cnZpdmFsIHJhdGVzIGZvciB0aGUgZGF0YSBgbHVzaXRhbmlhLmNzdmAgb24gPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgpUaGlzIGlzIGEgdGFidWxhdGVkIHZlcnNpb24gb2YgdGhlIGRhdGEsIGluIHRoZSBzYW1lIGZvcm1hdCBhcyBgdGl0YW5pY2AuIFRoZSBjb21wbGV0ZSBkYXRhc2V0CmlzIGF2YWlsYWJsZSBvbiBLYWdnbGUuCgpZb3UgY2FuIGZpbmQgb3V0IG1vcmUgYWJvdXQgdGhlIHNoaXB3cmVjayA8aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUk1TX0x1c2l0YW5pYT4KCgoKCgpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1GQUxTRSwgcHVybD1GQUxTRX0Ka25pdHI6OnB1cmwoImxlY3QyLlJtZCIpCgpgYGAKCgo=